package com.jrx.anytxn.transaction.timer.job.refund.step;

import com.alibaba.fastjson.JSONObject;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.uitl.BeanUtils;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.JsonUtils;
import com.jrx.anytxn.customer.entity.CmCustomerSecondLevel;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerSecondLevelMapper;
import com.jrx.anytxn.feign.api.request.TxnPayRefundReq;
import com.jrx.anytxn.feign.api.response.TxnPayRefundRes;
import com.jrx.anytxn.feign.api.service.IUserApiService;
import com.jrx.anytxn.feign.common.GateWayPredicate;
import com.jrx.anytxn.feign.gateway.bean.ApiResponse;
import com.jrx.anytxn.feign.gateway.response.CoreResponse20;
import com.jrx.anytxn.feign.gateway.service.IInnerGatewayService;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.dto.system.SystemParameterRes;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.service.system.ISystemParameterService;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.entity.TlPaymentAllocationLog;
import com.jrx.anytxn.transaction.entity.TlRefundLog;
import com.jrx.anytxn.transaction.entity.TlTransactionInfo;
import com.jrx.anytxn.transaction.entity.TlWechatRefundLog;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentAllocationLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlTransactionInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlWechatRefundLogMapper;
import com.jrx.anytxn.transaction.service.IDealGatewayService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 还款 - processor
 *
 * @author jing.xy
 */
@Service
public class RefundProcessor implements ItemProcessor<TlRefundLog, TlRefundLog> {

    Logger logger = LoggerFactory.getLogger(RefundProcessor.class);


    @Autowired
    private IOrganizationTableService organizationTableService;
    @Lazy
    @Autowired
    private IInnerGatewayService innerGatewayService;
    @Lazy
    @Autowired
    private IUserApiService iUserApiService;
    @Autowired
    private IDealGatewayService dealGatewayService;
    @Autowired
    private ISystemParameterService systemParameterService;
    @Autowired
    private ExtCmCustomerSecondLevelMapper extCmCustomerSecondLevelMapper;
    @Autowired
    private ExtTlWechatRefundLogMapper extTlWechatRefundLogMapper;
    @Autowired
    private ExtTlTransactionInfoMapper extTlTransactionInfoMapper;
    @Autowired
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Autowired
    private ISegmentService iSegmentService;


    @Override
    public TlRefundLog process(TlRefundLog log) throws Exception {
        logger.info("转账log:{}", BeanUtils.toMap(log));
        String tenantId = log.getTenantId();
        String customerId = log.getCustomerId();
        String organizationId = log.getOrganizationId();
        PrOrganizationTable organization = organizationTableService.findByOrId(organizationId, tenantId);
        Date businessDate = organization.getNextProcessingDate();//机构上 当天日期

        TlRefundLog updateLog = new TlRefundLog();
        updateLog.setId(log.getId());
        updateLog.setCustomerId(customerId);
        updateLog.setTenantId(tenantId);
        updateLog.setLaunchTime(DateUtils.getBusinessTime(new Date(), businessDate));
        updateLog.setSumStatus(log.getSumStatus());

        String result = null;
        String busCode = null;
        String busMsg = null;
        String dealState = null;
        // 走微信退款
        if (Constant.THIRD_CHANNEL.equalsIgnoreCase(log.getThirdChannel())) {

            logger.info("进入微信退款溢缴款");

            // 判断
            String payId = log.getPayId();
            List<TlWechatRefundLog> wechatRefundLogs = extTlWechatRefundLogMapper.queryByPayIdAndCustomerId(payId, tenantId, customerId, null);
            // 已有退款记录
            CmCustomerSecondLevel secondLevel = extCmCustomerSecondLevelMapper.selectByCustomerIdAndChannel(customerId, log.getChannel(), tenantId);
            BigDecimal amount = log.getAmount();
            if (CollectionUtils.isNotEmpty(wechatRefundLogs)) {
                wechatRefundLogs.stream()
                        .filter(refundLog -> !TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equalsIgnoreCase(refundLog.getSumStatus()))
                        .forEach(refundLog -> wechatRefund(tenantId, payId, secondLevel, refundLog));
                amount = BigDecimal.ZERO;
            } else {
                // 没有退款记录，则创建新的退款操作
                amount = wechatRefund(log, tenantId, customerId, organizationId, organization, payId, wechatRefundLogs, secondLevel, amount);
            }
            // 处理中或者失败的数据
            List<TlWechatRefundLog> deals = wechatRefundLogs.stream().filter(refundLog -> !TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equalsIgnoreCase(refundLog.getSumStatus())).collect(Collectors.toList());
            if (BigDecimal.ZERO.compareTo(amount) != 0 && deals.size() == 0) {
                result = "F";
                dealState = "F";
                busCode = "F";
                busMsg = "金额不平";
            } else if (deals.size() > 0) {
                result = "O";
                dealState = "O";
                busCode = "O";
                busMsg = "处理中";
            } else {
                result = "S";
                dealState = "S";
                busCode = "S";
                busMsg = "成功";
            }
        } else {
            logger.info("进入其他退款溢缴款");
            CoreResponse20 res = null;
            //扣款
            String paymentRequest06Json = dealGatewayService.buildRefundReq(log);
            GateWayPredicate<CoreResponse20> refund = () -> innerGatewayService.refund(paymentRequest06Json);
            try {
                res = refund.getRes(refund);
            } catch (Exception e) {
                logger.error("调用还款接口出现异常", e);
            }
            if (res == null) {
                updateLog.setCompleteTime(DateUtils.getBusinessTime(new Date(), businessDate));
                return updateLog;
            }
            result = res.getDealResult();
            busCode = res.getBusCode();
            busMsg = res.getBusMsg();
            dealState = res.getDealState();
        }

        boolean statusFlag = StringUtils.isBlank(result) || !(TransBizConstant.GATEWAY_DEAL_SUC.equals(result) || TransBizConstant.GATEWAY_DEAL_FAIL.equals(result));
        if (statusFlag) {
            logger.info("用户{}退款的处理结果{}", customerId, result + "&" + busMsg);
            return null;
        }
        //成功
        if (TransBizConstant.GATEWAY_DEAL_SUC.equals(result)) {
            updateLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_O);
        } else {
            //失败
            //updateLog.setRefNo(sequenceNumberService.generateRefNo(Constant.SEQUENCE_LENGTH));
            SystemParameterRes systemAcctBranch = systemParameterService.findByParamCode(TransBizConstant.TRANSFER_TIMES, tenantId);
            String transferTimes = systemAcctBranch.getParamValue();
            int times = Integer.parseInt(log.getTransferTimes()) + 1;
            if (Integer.parseInt(transferTimes) <= times) {
                updateLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_F);
            }
            updateLog.setTransferTimes(times + "");
        }

        updateLog.setReturnCode(busCode);
        updateLog.setReturnMsg(busMsg);
        updateLog.setReturnStatus(dealState);
        updateLog.setCompleteTime(DateUtils.getBusinessTime(new Date(), businessDate));

        return updateLog;
    }

    /**
     * 微信退款
     * @param log
     * @param tenantId
     * @param customerId
     * @param organizationId
     * @param organization
     * @param payId
     * @param wechatRefundLogs
     * @param secondLevel
     * @param amount
     * @return
     */
    public BigDecimal wechatRefund(TlRefundLog log, String tenantId, String customerId, String organizationId, PrOrganizationTable organization, String payId, List<TlWechatRefundLog> wechatRefundLogs,
                                   CmCustomerSecondLevel secondLevel, BigDecimal amount) {
        // 1.根据之前的还款流水找出退款的原借据编号
        TlTransactionInfo tlTransactionInfo = extTlTransactionInfoMapper.selectBySeqNo(payId, customerId, tenantId);
        // 原交易借据编号
        String loanId = tlTransactionInfo.getLoanId();
        // 2.获取都有所有的还款分配交易记录
        TlPaymentAllocationLog tlPaymentAllocationLog = new TlPaymentAllocationLog();
        tlPaymentAllocationLog.setCustomerId(customerId);
        tlPaymentAllocationLog.setTenantId(tenantId);
        tlPaymentAllocationLog.setLoanId(loanId);

        tlPaymentAllocationLog.setAccountType(TransBizConstant.PAYMENT_ALLOCATION_TYPE_09);
        List<TlPaymentAllocationLog> allocationLogList = extTlPaymentAllocationLogMapper.selectListByLoanId(tenantId,customerId,loanId,TransBizConstant.PAYMENT_ALLOCATION_TYPE_09,
                Arrays.asList(TransBizConstant.TXN_CODE_INST_PYMT,TransBizConstant.TXN_CODE_INST_PYMT_INTR,TransBizConstant.TXN_CODE_INST_PYMT_PEN_INTR,TransBizConstant.TXN_CODE_INST_PYMT_PEN_SEC,
                        TransBizConstant.TXN_CODE_INST_PYMT_INTR_SEC));
        // 3.算出每个还款分配流水能退款的钱
        Map<String, BigDecimal> repayMap = allocationLogList.stream()
                .sorted(Comparator.comparing(TlPaymentAllocationLog::getCreateTime).reversed())
                .collect(Collectors.groupingBy(TlPaymentAllocationLog::getSeqNo, CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount)));
        //boolean successFlag = true;
        // 4.循环调用微信退款接口
        for (Map.Entry<String, BigDecimal> entry : repayMap.entrySet()) {
            String seqNo = entry.getKey();
            List<TlWechatRefundLog> usedList = extTlWechatRefundLogMapper.queryBySeqNoAndLoanId(customerId, seqNo, loanId, tenantId, TransBizConstant.PAYMENT_LOG_SUM_STATUS_F);
            BigDecimal postingAmount = entry.getValue();
            BigDecimal usedAmt = usedList.stream().map(TlWechatRefundLog::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            // 之前总的还款金额 - 已经退款的金额 = 剩余可退金额
            BigDecimal availableAmt = postingAmount.subtract(usedAmt);
            if (BigDecimal.ZERO.compareTo(amount) < 0 && BigDecimal.ZERO.compareTo(availableAmt) < 0 ) {
                TlWechatRefundLog refundLog = new TlWechatRefundLog();
                refundLog.setTenantId(tenantId);
                refundLog.setOrganizationId(organizationId);
                refundLog.setProductId(log.getProductId());
                refundLog.setChannel(log.getChannel());
                refundLog.setCustomerId(customerId);
                refundLog.setPaymentSeqNo(log.getPayId());
                refundLog.setOriginalSeqNo(seqNo);
                refundLog.setLoanId(loanId);
                // min(剩余可退金额,单次退款金额)  取小值
                BigDecimal refundAmt = amount.compareTo(availableAmt) > 0 ? availableAmt : amount;
                amount = amount.subtract(refundAmt);
                // 单次退款金额
                refundLog.setAmount(refundAmt);
                refundLog.setCurrency("CNY");
                refundLog.setPayId(payId);
                refundLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_O);
                refundLog.setSumMsg("");
                refundLog.setBusinessDate(organization.getToday());
                refundLog.setCreateTime(new Date());
                refundLog.setUpdateTime(new Date());
                refundLog.setRecordVersionNumber(1);
                wechatRefund(tenantId, payId, secondLevel, refundLog);
                wechatRefundLogs.add(refundLog);
                //if (!TransBizConstant.PAYMENT_LOG_SUM_STATUS_S.equalsIgnoreCase(refundLog.getSumStatus())) {
                //    successFlag = false;
                //    result = refundLog.getSumStatus();
                //    dealState = refundLog.getSumStatus();
                //    busCode = refundLog.getSumStatus();
                //    busMsg = refundLog.getSumMsg();
                //}
            }
        }
        return amount;
    }

    public static void main(String[] args) throws InterruptedException {
        List<TlPaymentAllocationLog> allocationLogList = new ArrayList<>();
        TlPaymentAllocationLog a = new TlPaymentAllocationLog();
        a.setSeqNo("1L");
        a.setCreateTime(new Date());
        Thread.sleep(1000);
        TlPaymentAllocationLog b = new TlPaymentAllocationLog();
        b.setSeqNo("2L");
        b.setCreateTime(new Date());
        Thread.sleep(1000);
        TlPaymentAllocationLog c = new TlPaymentAllocationLog();
        c.setSeqNo("3L");
        c.setCreateTime(new Date());
        Thread.sleep(1000);
        TlPaymentAllocationLog d = new TlPaymentAllocationLog();
        d.setSeqNo("4L");
        d.setCreateTime(new Date());
        TlPaymentAllocationLog e = new TlPaymentAllocationLog();
        e.setSeqNo("4L");
        e.setCreateTime(new Date());
        TlPaymentAllocationLog f = new TlPaymentAllocationLog();
        f.setSeqNo("1L");
        f.setCreateTime(new Date());
        allocationLogList.add(b);
        allocationLogList.add(a);
        allocationLogList.add(d);
        allocationLogList.add(c);
        allocationLogList.add(f);
        allocationLogList.add(e);
        allocationLogList.stream().forEach(aa->{
            aa.setSeqNo("111");
        });
        System.out.println(JSONObject.toJSONString(allocationLogList));
        //List<String> loanIds = Arrays.asList("3L","5L");

        //Map<String, List<TlPaymentAllocationLog>> repayMap = allocationLogList.stream()
        //        .sorted(Comparator.comparing(TlPaymentAllocationLog::getCreateTime).reversed())
        //        .collect(Collectors.groupingBy(TlPaymentAllocationLog::getSeqNo));
        //
        //for (Map.Entry<String, List<TlPaymentAllocationLog>> entry : repayMap.entrySet()) {
        //    String key = entry.getKey();
        //    System.out.println(key);
        //}

        //allocationLogList.removeIf(e -> loanIds.contains(e.getLoanId()));
        //List<TlPaymentAllocationLog> collect = allocationLogList.stream().sorted(Comparator.comparing(TlPaymentAllocationLog::getCreateTime).reversed()).collect(Collectors.toList());
        //for (TlPaymentAllocationLog  s : collect) {
        //    System.out.println(s.getLoanId()+"-------------"+DateUtils.format(s.getCreateTime(),DateUtils.DATETIME_PATTERN));
        //
        //}

    }

    /**
     * 微信退款操作
     * @param tenantId
     * @param payId
     * @param secondLevel
     * @param refundLog
     */
    public void wechatRefund(String tenantId, String payId, CmCustomerSecondLevel secondLevel, TlWechatRefundLog refundLog) {
        TxnPayRefundReq req = new TxnPayRefundReq();
        req.setTenantId(tenantId);
        req.setChannelNo(refundLog.getChannel());
        req.setUserId(secondLevel.getUserId());
        req.setTxnRefundSeqNo(payId);
        req.setPaymentSeqNo(refundLog.getOriginalSeqNo());
        req.setTxnAmt(refundLog.getAmount());
        ApiResponse<TxnPayRefundRes> txnPayRefundRes;
        try {
            txnPayRefundRes = iUserApiService.txnPayRefund(JsonUtils.toJSon(req));
        } catch (Exception e) {
            e.printStackTrace();
            logger.warn("微信退款API失败,响应为空或者非成功码值{}", e.getMessage());
            refundLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_O);
            return;
        }
        String status = TransBizConstant.PAYMENT_LOG_SUM_STATUS_O;
        String message = "微信退款处理中";
        if (Objects.isNull(txnPayRefundRes) || TxnRespCode.SUCCESS.getCode() != txnPayRefundRes.getCode()) {
            logger.warn("微信退款API失败,响应为空或者非成功码值");
            if (Objects.nonNull(txnPayRefundRes) && Objects.nonNull(txnPayRefundRes.getData())) {
                status = txnPayRefundRes.getData().getStatus();
                message = txnPayRefundRes.getMessage();
            } else {
                message = "微信退款API失败,响应为空或者非成功码值";
            }
        }
        refundLog.setSumStatus(status);
        refundLog.setSumMsg(message);
        if (Objects.nonNull(refundLog.getId())) {
            TlWechatRefundLog log = new TlWechatRefundLog();
            log.setId(refundLog.getId());
            log.setTenantId(refundLog.getTenantId());
            log.setSumStatus(status);
            log.setSumMsg(message);
            extTlWechatRefundLogMapper.updateByPrimaryKeySelective(log);
        } else {
            try {
                refundLog.setId(iSegmentService.getId("TlWechatRefundLog"));
            } catch (SegmentException e) {
                e.printStackTrace();
                logger.error("TlWechatRefundLog生成主键出错");
            }
            extTlWechatRefundLogMapper.insertSelective(refundLog);
        }
    }
}
